/*
File: table.h
Authors: Team 3- Sam Stewart, Dylan McDougall, Stephen Ten Eyck
Date: 06/05/12
CSCE 315- 100 Summer 1 2012
*/

#ifndef TABLE_H_
#define TABLE_H_

/*
The Table class is the building block of the custom database engine.
*/

//includes
#include "std_lib_facilities.h"

//defines
#define INTEGER 0				//used for types
#define STRING 1				//used for types

//check wether the string is an int
bool integerCheck(string str){
    istringstream inpStream(str);
    double inpValue = 0.0;
    if(inpStream >> inpValue){
        return true;
    }
    else return false;    
}


/*==========================================================================*/
/*								TABLE CLASS									*/
/*==========================================================================*/

class Table{
	//private members
	string name;
	vector<string> attrList;
	vector<int> types;					//STRING or INTEGER
	vector<string> key;
	vector<int> keyNum;					//the number of the attributes corresponding to the key
	vector< vector<string> > values;	//actual values stored in the table
	
	//private functions
	bool checkKey(int tuple, vector<string> userKey);	//checks if the key exists in the given tuple
	

public:

	//constructors
	Table();			//default not used
	Table(string name_, vector<string> attrList_, vector<int> types_, vector<string> key_);
	
	//public functions
	int numAttr();
	int numTuples();
	string getName();
	string getValue(int tuple, string attr);	//string attr is the attr name
	string getValue(int tuple, int attr);		//int attr is the number corresponding to the attr in the attrList
	vector<string> getKey();					//gets the table key
	vector<string> getKeyFromTuple(int tuple);	//gets the user key corresponding to the given tuple
	int numOfKeys();
	vector<int> getTypes();						//return the list of types corresponding to the attrList
	bool attrInTuple(vector<string> attr, vector<string> value, int tuple);
	void addTuple(vector<string> attrs);
	vector<string> getAttrList();
	vector<string> getTuple(int tuple);
	void deleteTuple(vector<string> deleteKey);
	void updateName(string newName);			//update the table name
	void updateAttribute(vector<string> key, string attrOld, string attrNew);
	void print();
};

bool Table::checkKey(int tuple, vector<string> userKey){
	if(tuple > numTuples()){
		error("Tuple out of range.\n");
	}
	if(userKey.size() != key.size()){
		error("Invalid key size.\n");
	}
	vector<int> keyMatch(key.size());		//acts as a bool vector checking each key
	for(int i=0; i<keyMatch.size(); i++){
		keyMatch[i] = 0;
	}
	for(int i=0; i<key.size(); i++){
		if(userKey[i] == values[tuple][keyNum[i]]){
			keyMatch[i] = 1;
		}
	}
	for(int i=0; i<keyMatch.size(); i++){
		if(keyMatch[i] == 0){
			return false;
		}
	}
	return true;
}

//constructor
Table::Table(string name_, vector<string> attrList_, vector<int> types_, vector<string> key_){
	name = name_;
	attrList = attrList_;
	
	if(attrList_.size() != types_.size()){
		error("attrList does not match types size\n");
	}
	
	types = types_;
	
	//make sure keys match
	vector<int> keyMatch(key_.size());
	for(int i=0; i<keyMatch.size(); i++){
		keyMatch[i] = 0;
	}
	for(int i=0; i<key_.size(); i++){
		for(int j=0; j<attrList.size(); j++){
			if(key_[i] == attrList[j]){
				keyNum.push_back(j);
				keyMatch[i] = 1;
			}
		}
	}
	for(int i=0; i<key_.size(); i++){
		if(keyMatch[i] == 0){
			error("Key does not match attribute list.\n");
		}
	}
	key = key_;
}

int Table::numAttr(){
	return attrList.size();
}

int Table::numTuples(){
	values.size();
}

string Table::getName(){
	return name;
}

string Table::getValue(int tuple, string attr){
	int attrNum = -1;
	for(int i=0; i<attrList.size(); i++){
		if(attrList[i] == attr){
			attrNum = i;
		}
	}
	if(attrNum == -1){
		error("No matching attribute.\n");
	}
	return values[tuple][attrNum];
}

string Table::getValue(int tuple, int attr){
	return values[tuple][attr];
}

vector<string> Table::getKey(){
	return key;
}

vector<string> Table::getKeyFromTuple(int tuple){
	if(tuple >= numTuples()){
		error("getKey: tuple out of table range.");
	}
	vector<string> tempKey;
	for(int i=0; i<key.size(); i++){			//key list
		for(int j=0; j<attrList.size(); j++){	//attr list
			if(key[i] == attrList[j]){
				tempKey.push_back(values[tuple][j]);
			}
		}
	}
	return tempKey;
}

int Table::numOfKeys(){
	return key.size();
}

vector<int> Table::getTypes(){
	return types;
}

//tests whether the given value corresponding to the given attr is in the given tuple
bool Table::attrInTuple(vector<string> attr, vector<string> value, int tuple){
	vector<int> attrNum;
	for(int i=0; i<attr.size(); i++){
		attrNum.push_back(-1);
	}
	for(int i=0; i<attr.size(); i++){
		for(int j=0; j<attrList.size(); j++){
			if(attr[i] == attrList[j]){
				attrNum[i] = j;
			}
		}
	}
	for(int i=0; i<attrNum.size(); i++){
		if(attrNum[i] == -1){
			return false;
		}
	}
	for(int i=0; i<value.size(); i++){
		if(value[i] != values[tuple][attrNum[i]]){
			return false;
		}
	}
	return true;
}

void Table::addTuple(vector<string> attrs){
	if(attrs.size() != numAttr()){
		error("Number of attributes does not match attribute list.\n");
	}
	for(int i=0; i<types.size(); i++){
		if(types[i] == INTEGER){
			if(!integerCheck(attrs[i])){
				error("Invalid attribute type\n");
			}
		}
	}
	values.push_back(attrs);
}

vector<string> Table::getAttrList(){
	return attrList;
}

vector<string> Table::getTuple(int tuple){
	return values[tuple];
}

//delete the tuple corresponding to the given user key
void Table::deleteTuple(vector<string> deleteKey){
	if(deleteKey.size() != key.size()){
		error("Key sizes don't match\n");
	}
	for(int i=0; i<numTuples(); i++){
		if(checkKey(i, deleteKey)){
			values.erase(values.begin()+i);
			return;
		}
	}
	error("No matching key");
}

void Table::updateName(string newName){
	name = newName;
}

void Table::updateAttribute(vector<string> key, string attrOld, string attrNew){
	for(int i=0; i<numTuples(); i++){
		if(checkKey(i, key)){
			for(int j=0; j<attrList.size(); j++){
				if(values[i][j] == attrOld){
					values[i][j] = attrNew;
					return;
				}
			}
		}
	}
	error("UpdateAttribute: Attribute not found.");
}

void Table::print(){
	cout << endl << name << endl << '=';
	for(int i=0; i<numAttr(); i++){
		cout << "================";
	}
	cout << endl;
	for(int i=0; i<numAttr(); i++){
		cout << '|' << left << setw(15) << attrList[i];
	}
	cout << "|\n";
	for(int i=0; i<numAttr(); i++){
		string type;
		if(types[i] == INTEGER){
			type = "INTEGER";
		}
		else{
			type = "VARCHAR";
		}
		cout << '|' << left << setw(15) << type;
	}
	cout << "|\n";
	for(int i=0; i<numAttr(); i++){
		cout << "|---------------";
	}
	cout << "|\n";
	for(int i=0; i<numTuples(); i++){
		for(int j=0; j<numAttr(); j++){
			cout << '|' << left << setw(15) << values[i][j];
		}
		cout << '|' << endl;
	}
	for(int i=0; i<numAttr(); i++){
		cout << "================";
	}
	cout << "=\n\n";
}

#endif
